package com.r2coding.server.modules.file.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Splitter;
import com.r2coding.core.constants.Constants;
import com.r2coding.core.exception.Pan4jException;
import com.r2coding.core.utils.FileUtils;
import com.r2coding.core.utils.IdUtil;
import com.r2coding.server.common.event.file.DeleteFileEvent;
import com.r2coding.server.common.utils.HttpUtil;
import com.r2coding.server.common.utils.UserIdUtil;
import com.r2coding.server.modules.file.constants.FileConstants;
import com.r2coding.server.modules.file.context.CreateFolderContext;
import com.r2coding.server.modules.file.context.FileChunkMergeAndSaveContext;
import com.r2coding.server.modules.file.context.FileChunkMergeContext;
import com.r2coding.server.modules.file.context.FileChunkUploadContext;
import com.r2coding.server.modules.file.context.FileDownloadContext;
import com.r2coding.server.modules.file.context.FilePreviewContext;
import com.r2coding.server.modules.file.context.FileSaveContext;
import com.r2coding.server.modules.file.context.QueryFolderTreeContext;
import com.r2coding.server.modules.file.converter.FileConverter;
import com.r2coding.server.modules.file.entity.File;
import com.r2coding.server.modules.file.entity.FileChunk;
import com.r2coding.server.modules.file.entity.UserFile;
import com.r2coding.server.modules.file.enums.DelFlagEnum;
import com.r2coding.server.modules.file.enums.FileTypeEnum;
import com.r2coding.server.modules.file.enums.FolderFlagEnum;
import com.r2coding.server.modules.file.mapper.UserFileMapper;
import com.r2coding.server.modules.file.po.CopyFilePo;
import com.r2coding.server.modules.file.po.DeleteFilePo;
import com.r2coding.server.modules.file.po.FileChunkMergePo;
import com.r2coding.server.modules.file.po.FileChunkUploadPo;
import com.r2coding.server.modules.file.po.FileUploadPo;
import com.r2coding.server.modules.file.po.QueryUploadedChunksPo;
import com.r2coding.server.modules.file.po.SecUploadPo;
import com.r2coding.server.modules.file.po.TransferFilePo;
import com.r2coding.server.modules.file.po.UpdateFilenamePo;
import com.r2coding.server.modules.file.service.IFileChunkService;
import com.r2coding.server.modules.file.service.IFileService;
import com.r2coding.server.modules.file.service.IUserFileService;
import com.r2coding.server.modules.file.vo.FileChunkUploadVo;
import com.r2coding.server.modules.file.vo.FolderTreeNodeVo;
import com.r2coding.server.modules.file.vo.UploadedChunkVo;
import com.r2coding.server.modules.file.vo.UserFileVo;
import com.r2coding.storage.engine.core.StorageEngine;
import com.r2coding.storage.engine.core.context.ReadFileContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.assertj.core.util.Lists;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author 98k灬
 * @version v1.0.0
 * @Description : 功能描述
 * @Create on : 2023/10/4 12:36
 **/
@Slf4j
@Service
public class IUserFileServiceImpl extends ServiceImpl<UserFileMapper, UserFile> implements IUserFileService, ApplicationContextAware {
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Autowired
    private IFileService fileService;

    @Autowired
    private IFileChunkService fileChunkService;

    @Autowired
    private StorageEngine storageEngine;

    @Autowired
    private FileConverter fileConverter;

    /**
     * 创建文件夹信息
     *
     * @param createFolderContext 文件夹信息
     * @return 文件夹id
     */
    @Override
    public Long createFolder(CreateFolderContext createFolderContext) {
        return saveUserFile(createFolderContext.getParentId(),
                createFolderContext.getFolderName(),
                FolderFlagEnum.YES,
                0,
                null,
                createFolderContext.getUserId(),
                null);
    }

    /**
     * 获取用户的文件列表
     *
     * @param parentId  父ID
     * @param fileTypes 文件类型
     * @return 文件列表
     */
    @Override
    public List<UserFileVo> getFileList(String parentId, String fileTypes) {
        Long userId = UserIdUtil.get();
        Long realParentId = IdUtil.decrypt(parentId);
        List<Integer> fileTypeArray = null;
        if (!Objects.equals(FileConstants.ALL_FILE_TYPE, fileTypes)) {
            fileTypeArray = Splitter.on(Constants.COMMON_SEPARATOR).splitToList(fileTypes).stream().map(Integer::valueOf).collect(Collectors.toList());
        }

        LambdaQueryWrapper<UserFile> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserFile::getDelFlag, DelFlagEnum.NO.getCode());
        wrapper.eq(UserFile::getUserId, userId);
        wrapper.eq(UserFile::getParentId, realParentId);
        if (CollectionUtils.isNotEmpty(fileTypeArray)) {
            wrapper.in(UserFile::getFileType, fileTypeArray);
        }
        List<UserFile> userFiles = this.baseMapper.selectList(wrapper);
        if (CollectionUtils.isEmpty(userFiles)) {
            return null;
        }

        return userFiles.stream().map(item -> UserFileVo.builder()
                .fileType(item.getFileType())
                .fileSizeDesc(item.getFileSizeDesc())
                .updateTime(item.getUpdateTime())
                .filename(item.getFilename())
                .folderFlag(item.getFolderFlag())
                .parentId(item.getParentId())
                .fileId(item.getFileId())
                .build()).collect(Collectors.toList());
    }

    /**
     * 文件重命名
     *
     * @param updateFilenamePo 请求信息
     */
    @Override
    public void updateFilename(UpdateFilenamePo updateFilenamePo) {
        Long realFileId = IdUtil.decrypt(updateFilenamePo.getFileId());
        String newFilename = updateFilenamePo.getNewFilename();
        Long userId = UserIdUtil.get();

        LambdaQueryWrapper<UserFile> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserFile::getUserId, userId)
                .eq(UserFile::getFileId, realFileId)
                .eq(UserFile::getDelFlag, DelFlagEnum.NO.getCode());
        UserFile userFile = Optional.ofNullable(this.baseMapper.selectOne(wrapper)).orElseThrow(() ->
                new Pan4jException("文件不存在"));

        if (StringUtils.equals(newFilename, userFile.getFilename())) {
            throw new Pan4jException("请换一个新的文件名称来修改");
        }

        LambdaQueryWrapper<UserFile> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserFile::getParentId, userFile.getParentId())
                .eq(UserFile::getFilename, newFilename)
                .eq(UserFile::getDelFlag, DelFlagEnum.NO.getCode());
        Integer count = this.baseMapper.selectCount(queryWrapper);
        if (count > Constants.ZERO_INT) {
            throw new Pan4jException("该文件名称已被占用");
        }

        userFile.setFilename(newFilename).setUpdateTime(new Date());
        this.baseMapper.updateById(userFile);
    }

    /**
     * 批量删除文件
     *
     * @param deleteFilePo 请求信息
     */
    @Override
    public void deleteFile(DeleteFilePo deleteFilePo) {
        // 检查
        String fileIds = deleteFilePo.getFileIds();
        Long userId = UserIdUtil.get();
        Set<Long> realFileIdSet = Splitter.on(Constants.COMMON_SEPARATOR)
                .splitToList(fileIds).stream().map(IdUtil::decrypt).collect(Collectors.toSet());

        List<UserFile> userFileList = this.baseMapper.selectBatchIds(realFileIdSet);
        if (CollectionUtils.size(userFileList) != CollectionUtils.size(realFileIdSet)) {
            throw new Pan4jException("存在不合法的文件ID");
        }

        Set<Long> userIdSet = userFileList.stream().map(UserFile::getUserId).collect(Collectors.toSet());
        if (CollectionUtils.size(userIdSet) != 1) {
            throw new Pan4jException("存在不合法的文件ID");
        }

        Long dbUserId = userIdSet.stream().findFirst().get();
        if (!Objects.equals(dbUserId, userId)) {
            throw new Pan4jException("当前登录用户没有删除该文件的权限");
        }

        // 批量删除
        this.baseMapper.deleteBatchIds(realFileIdSet);

        // 发布事件
        DeleteFileEvent fileEvent = new DeleteFileEvent(this, realFileIdSet);
        applicationContext.publishEvent(fileEvent);
    }

    /**
     * 文件秒传
     *
     * @param secUploadPo 请求信息
     * @return 是否秒传成功
     */
    @Override
    public boolean secUpload(SecUploadPo secUploadPo) {
        Long userId = UserIdUtil.get();
        String identifier = secUploadPo.getIdentifier();
        String filename = secUploadPo.getFilename();
        Long realParentId = IdUtil.decrypt(secUploadPo.getParentId());

        File file = this.getFileByUserIdAndIdentifier(userId, identifier);
        if (Objects.isNull(file)) {
            return false;
        }

        this.saveUserFile(realParentId,
                filename,
                FolderFlagEnum.NO,
                FileTypeEnum.getFileTypeCode(FileUtils.getFileSuffix(filename)),
                file.getFileId(),
                userId,
                file.getFileSizeDesc());

        return true;
    }

    /**
     * 单文件上传
     *
     * @param fileUploadPo 请求信息
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void upload(FileUploadPo fileUploadPo) {
        String filename = fileUploadPo.getFilename();
        Long userId = UserIdUtil.get();
        Long realParentId = IdUtil.decrypt(fileUploadPo.getParentId());

        FileSaveContext fileSaveContext = FileSaveContext.builder()
                .totalSize(fileUploadPo.getTotalSize())
                .identifier(fileUploadPo.getIdentifier())
                .filename(filename)
                .userId(userId)
                .file(fileUploadPo.getFile())
                .build();
        fileService.saveFile(fileSaveContext);

        File record = fileSaveContext.getRecord();
        this.saveUserFile(realParentId,
                filename,
                FolderFlagEnum.NO,
                FileTypeEnum.getFileTypeCode(FileUtils.getFileSuffix(filename)),
                record.getFileId(),
                userId,
                record.getFileSizeDesc());
    }

    /**
     * 分片上传
     *
     * @param fileChunkUploadPo 请求信息
     * @return 返回信息
     */
    @Override
    public FileChunkUploadVo chunkUpload(FileChunkUploadPo fileChunkUploadPo) {
        FileChunkUploadContext fileChunkUploadContext = new FileChunkUploadContext();
        BeanUtils.copyProperties(fileChunkUploadPo, fileChunkUploadContext);
        fileChunkUploadContext.setUserId(UserIdUtil.get());
        fileChunkService.saveChunkFile(fileChunkUploadContext);
        return FileChunkUploadVo.builder()
                .mergeFlag(fileChunkUploadContext.getMergeFlagEnum().getCode())
                .build();
    }

    /**
     * 查询用户已上传的文件分片列表
     *
     * @param po 请求实体
     * @return 用户已上传的文件分片列表
     */
    @Override
    public UploadedChunkVo getUploadedChunks(QueryUploadedChunksPo po) {
        LambdaQueryWrapper<FileChunk> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(FileChunk::getChunkNumber);
        wrapper.eq(FileChunk::getIdentifier, po.getIdentifier())
                .eq(FileChunk::getCreateUser, UserIdUtil.get())
                .eq(FileChunk::getExpirationTime, new Date());
        List<Integer> uploadedChunks = fileChunkService.listObjs(wrapper, value -> (Integer) value);
        return UploadedChunkVo.builder()
                .uploadedChunks(uploadedChunks)
                .build();
    }

    /**
     * 文件分片合并
     *
     * @param fileChunkMergePo 请求实体
     */
    @Override
    public void mergeFile(FileChunkMergePo fileChunkMergePo) {
        FileChunkMergeContext context = new FileChunkMergeContext();
        BeanUtils.copyProperties(fileChunkMergePo, context);
        context.setUserId(UserIdUtil.get());

        this.mergeFileChunkAndSaveFile(context);
        String filename = fileChunkMergePo.getFilename();
        this.saveUserFile(IdUtil.decrypt(fileChunkMergePo.getParentId()),
                filename,
                FolderFlagEnum.NO,
                FileTypeEnum.getFileTypeCode(FileUtils.getFileSuffix(filename)),
                context.getRecord().getFileId(),
                context.getUserId(),
                context.getRecord().getFileSizeDesc());
    }

    /**
     * 文件下载
     *
     * @param context 文件下载上下文实体
     */
    @Override
    public void download(FileDownloadContext context) {
        UserFile record = getById(context.getFileId());
        this.checkOperatePermission(record, context.getUserId());
        if (this.checkIsFolder(record)) {
            throw new Pan4jException("文件夹暂不支持下载");
        }
        this.doDownload(record, context.getResponse());
    }

    /**
     * 文件预览
     *
     * @param context 文件预览上下文实体
     */
    @Override
    public void preview(FilePreviewContext context) {
        UserFile record = getById(context.getFileId());
        this.checkOperatePermission(record, context.getUserId());
        if (this.checkIsFolder(record)) {
            throw new Pan4jException("文件夹暂不支持下载");
        }
        this.doPreview(record, context.getResponse());
    }

    /**
     * 查询用户的文件夹树
     *
     * @param context 上下文实体
     * @return 用户的文件夹树
     */
    @Override
    public List<FolderTreeNodeVo> getFolderTree(QueryFolderTreeContext context) {
        List<UserFile> folderRecords = this.queryFolderRecords(context.getUserId());
        return this.assembleFolderTreeNodeVoList(folderRecords);
    }

    /**
     * 文件转移
     *
     * @param transFerFilePo 请求体
     */
    @Override
    public void transfer(TransferFilePo transFerFilePo) {
        Long parentId = IdUtil.decrypt(transFerFilePo.getTargetParentId());
        List<Long> fileIdList = Splitter.on(Constants.COMMON_SEPARATOR).splitToList(transFerFilePo.getFileIds())
                .stream().map(IdUtil::decrypt).collect(Collectors.toList());

        if (!checkIsFolder(getById(parentId))) {
            throw new Pan4jException("目标文件不是一个文件夹");
        }


        List<UserFile> userFiles = this.baseMapper.selectBatchIds(fileIdList);
        if (checkIsChildFolder(userFiles, parentId, UserIdUtil.get())) {
            throw new Pan4jException("目标文件夹ID不能是选中文件列表的文件夹ID及其子文件夹ID");
        }

        userFiles.stream().forEach( record -> {
            record.setParentId(parentId);
            record.setUserId(UserIdUtil.get());
            record.setCreateUser(UserIdUtil.get());
            record.setUpdateUser(UserIdUtil.get());
            record.setCreateTime(new Date()).setUpdateTime(new Date());
            handleDuplicateFilename(record);
        });

        updateBatchById(userFiles);
    }

    /**
     * 文件复制
     *
     * @param copyFilePo 请求体
     */
    @Override
    public void copy(CopyFilePo copyFilePo) {
        Long parentId = IdUtil.decrypt(copyFilePo.getTargetParentId());
        List<Long> fileIdList = Splitter.on(Constants.COMMON_SEPARATOR).splitToList(copyFilePo.getFileIds())
                .stream().map(IdUtil::decrypt).collect(Collectors.toList());

        if (!checkIsFolder(getById(parentId))) {
            throw new Pan4jException("目标文件不是一个文件夹");
        }


        List<UserFile> userFiles = this.baseMapper.selectBatchIds(fileIdList);
        if (checkIsChildFolder(userFiles, parentId, UserIdUtil.get())) {
            throw new Pan4jException("目标文件夹ID不能是选中文件列表的文件夹ID及其子文件夹ID");
        }

        if (CollectionUtils.isNotEmpty(userFiles)) {
            List<UserFile> allRecords = Lists.newArrayList();
            userFiles.forEach(record -> assembleCopyChildRecord(allRecords, record, parentId, IdUtil.get()));
            saveBatch(allRecords);
        }
    }

    /****************************************** private *********************************************/

    private void assembleCopyChildRecord(List<UserFile> allRecords, UserFile record, Long targetParentId, Long userId) {
        Long newFileId = IdUtil.get();
        Long oldFileId = record.getFileId();

        record.setParentId(targetParentId);
        record.setFileId(newFileId);
        record.setUserId(userId);
        record.setCreateUser(userId);
        record.setCreateTime(new Date());
        record.setUpdateUser(userId);
        record.setUpdateTime(new Date());
        handleDuplicateFilename(record);

        allRecords.add(record);

        if (checkIsFolder(record)) {
            List<UserFile> childRecords = findChildRecords(oldFileId);
            if (CollectionUtils.isEmpty(childRecords)) {
                return;
            }
            childRecords.stream().forEach(childRecord -> assembleCopyChildRecord(allRecords, childRecord, newFileId, userId));
        }
    }

    private List<UserFile> findChildRecords(Long parentId) {
        LambdaQueryWrapper<UserFile> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserFile::getParentId, parentId)
                .eq(UserFile::getDelFlag, DelFlagEnum.NO.getCode());
        return list(wrapper);
    }

    private boolean checkIsChildFolder(List<UserFile> userFiles, Long parentId, Long userId) {
        userFiles = userFiles.stream().filter(record -> Objects.equals(record.getFolderFlag(), FolderFlagEnum.YES.getCode())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(userFiles)) {
            return false;
        }

        List<UserFile> folderRecords = queryFolderRecords(userId);
        Map<Long, List<UserFile>> folderrecordMap = folderRecords.stream().collect(Collectors.groupingBy(UserFile::getParentId));

        List<UserFile> unAvailableFolderRecords = Lists.newArrayList();
        unAvailableFolderRecords.addAll(userFiles);
        userFiles.stream().forEach(record -> findAllChildFolderRecords(unAvailableFolderRecords, folderrecordMap, record));

        List<Long> unAvalableFolderRecordIds = unAvailableFolderRecords.stream().map(UserFile::getFileId).collect(Collectors.toList());

        return unAvalableFolderRecordIds.contains(parentId);
    }

    private void findAllChildFolderRecords(List<UserFile> unAvailableFolderRecords, Map<Long, List<UserFile>> folderrecordMap, UserFile record) {
        if (Objects.isNull(record)) {
            return;
        }
        List<UserFile> childFolderRecords = folderrecordMap.get(record.getFileId());
        if (CollectionUtils.isEmpty(childFolderRecords)) {
            return;
        }
        unAvailableFolderRecords.addAll(childFolderRecords);
        childFolderRecords.stream().forEach(childRecord -> findAllChildFolderRecords(unAvailableFolderRecords, folderrecordMap, childRecord));
    }

    // 拼装文件夹树列表
    private List<FolderTreeNodeVo> assembleFolderTreeNodeVoList(List<UserFile> folderRecords) {
        if (CollectionUtils.isEmpty(folderRecords)) {
            return Lists.newArrayList();
        }

        List<FolderTreeNodeVo> mappedFolderTreeNodeVoList =
                folderRecords.stream().map(fileConverter::userFile2FolderTreeNodeVO).collect(Collectors.toList());

        Map<Long, List<FolderTreeNodeVo>> mappedFolderTreeNodeVoMap =
                mappedFolderTreeNodeVoList.stream().collect(Collectors.groupingBy(FolderTreeNodeVo::getParentId));

        for (FolderTreeNodeVo node : mappedFolderTreeNodeVoList) {
            List<FolderTreeNodeVo> children = mappedFolderTreeNodeVoMap.get(node.getId());
            if (CollectionUtils.isNotEmpty(children)) {
                node.getChildren().addAll(children);
            }
        }

        return mappedFolderTreeNodeVoList.stream().
                filter(node -> Objects.equals(node.getParentId(), FileConstants.TOP_PARENT_ID)).collect(Collectors.toList());
    }

    private List<UserFile> queryFolderRecords(Long userId) {
        LambdaQueryWrapper<UserFile> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserFile::getUserId, userId)
                .eq(UserFile::getFolderFlag, FolderFlagEnum.YES.getCode())
                .eq(UserFile::getDelFlag, DelFlagEnum.NO.getCode());
        return this.baseMapper.selectList(wrapper);
    }

    private void doPreview(UserFile record, HttpServletResponse response) {
        File realFileRecord = fileService.getById(record.getRealFileId());
        if (Objects.isNull(realFileRecord)) {
            throw new Pan4jException("当前文件记录不存在");
        }

        this.addCommonResponseHeader(response, realFileRecord.getFilePreviewContentType());
        this.realFile2OutputStream(realFileRecord.getRealPath(), response);
    }

    private void checkOperatePermission(UserFile record, Long userId) {
        if (Objects.isNull(record)) {
            throw new Pan4jException("当前文件记录不存在");
        }

        if (!record.getUserId().equals(userId)) {
            throw new Pan4jException("您没有该文件的操作权限");
        }
    }

    private boolean checkIsFolder(UserFile record) {
        if (Objects.isNull(record)) {
            throw new Pan4jException("当前文件记录不存在");
        }
        return FolderFlagEnum.YES.getCode().equals(record.getFolderFlag());
    }

    private void mergeFileChunkAndSaveFile(FileChunkMergeContext context) {
        FileChunkMergeAndSaveContext fileChunkMergeAndSaveContext = new FileChunkMergeAndSaveContext();
        BeanUtils.copyProperties(context, fileChunkMergeAndSaveContext);
        fileService.mergeFileChunkAndSaveFile(fileChunkMergeAndSaveContext);
        context.setRecord(fileChunkMergeAndSaveContext.getRecord());
    }

    private void doDownload(UserFile record, HttpServletResponse response) {
        File realFileRecord = fileService.getById(record.getRealFileId());
        if (Objects.isNull(realFileRecord)) {
            throw new Pan4jException("当前文件记录不存在");
        }

        this.addCommonResponseHeader(response, MediaType.APPLICATION_OCTET_STREAM_VALUE);
        this.addDownloadAttribute(response, record, realFileRecord);
        this.realFile2OutputStream(realFileRecord.getRealPath(), response);
    }

    private void realFile2OutputStream(String realPath, HttpServletResponse response) {
        try {
            ReadFileContext context = ReadFileContext.builder()
                    .realPath(realPath)
                    .outputStream(response.getOutputStream())
                    .build();
            storageEngine.readFile(context);
        } catch (IOException exception) {
            exception.printStackTrace();
            throw new Pan4jException("文件下载失败");
        }

    }

    private void addDownloadAttribute(HttpServletResponse response, UserFile record, File realFileRecord) {
        try {
            response.addHeader(FileConstants.CONTENT_DISPOSITION_STR,
                    FileConstants.CONTENT_DISPOSITION_VALUE_PREFIX_STR + new String(record.getFilename().getBytes(FileConstants.GB2312_STR), FileConstants.IOS_8859_1_STR));
        } catch (UnsupportedEncodingException exception) {
            exception.printStackTrace();
            throw new Pan4jException("文件下载失败");
        }

        response.setContentLengthLong(Long.parseLong(realFileRecord.getFileSize()));
    }

    private void addCommonResponseHeader(HttpServletResponse response, String type) {
        response.reset();
        HttpUtil.addCorsResponseHeaders(response);
        response.setHeader(FileConstants.CONTENT_TYPE_STR, type);
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
    }

    private File getFileByUserIdAndIdentifier(Long userId, String identifier) {
        LambdaQueryWrapper<File> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(File::getCreateUser, userId)
                .eq(File::getIdentifier, identifier);
        List<File> fileList = fileService.list(wrapper);
        if (CollectionUtils.isEmpty(fileList)) {
            return null;
        }
        return fileList.get(Constants.ZERO_INT);
    }

    private Long saveUserFile(Long parentId,
                              String filename,
                              FolderFlagEnum folderFlagEnum,
                              Integer fileType,
                              Long realFileId,
                              Long userId,
                              String fileSizeDesc) {
        Date currentDate = new Date();
        UserFile userFile = UserFile.builder()
                .fileId(IdUtil.get())
                .userId(userId)
                .parentId(parentId)
                .realFileId(realFileId)
                .filename(filename)
                .folderFlag(folderFlagEnum.getCode())
                .fileSizeDesc(fileSizeDesc)
                .fileType(fileType)
                .delFlag(DelFlagEnum.NO.getCode())
                .createUser(userId)
                .createTime(currentDate)
                .updateUser(userId)
                .updateTime(currentDate)
                .build();
        this.handleDuplicateFilename(userFile);
        if (!save((userFile))) {
            throw new Pan4jException("保存文件信息失败");
        }
        return userFile.getFileId();
    }

    private void handleDuplicateFilename(UserFile entity) {
        String filename = entity.getFilename(),
                newFilenameWithoutSuffix,
                newFilenameSuffix;
        int newFilenamePointPosition = filename.lastIndexOf(Constants.POINT_STR);
        if (newFilenamePointPosition == Constants.MINUS_ONE_INT) {
            newFilenameWithoutSuffix = filename;
            newFilenameSuffix = StringUtils.EMPTY;
        } else {
            newFilenameWithoutSuffix = filename.substring(Constants.ZERO_INT, newFilenamePointPosition);
            newFilenameSuffix = filename.replace(newFilenameWithoutSuffix, StringUtils.EMPTY);
        }
        int count = this.getDuplicateFilename(entity, newFilenameWithoutSuffix);
        if (count == 0) {
            return;
        }
        entity.setFilename(this.assembleNewFilename(newFilenameWithoutSuffix, count, newFilenameSuffix));
    }

    private String assembleNewFilename(String newFilenameWithoutSuffix, int count, String newFilenameSuffix) {
        return newFilenameWithoutSuffix +
                FileConstants.CN_LEFT_PARENTHESES_STR +
                count +
                FileConstants.CN_RIGHT_PARENTHESES_STR +
                newFilenameSuffix;
    }

    /**
     * 查找同一文件夹下面的同名文件数量
     */
    private int getDuplicateFilename(UserFile userFile, String newFilenameWithoutSuffix) {
        LambdaQueryWrapper<UserFile> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserFile::getParentId, userFile.getParentId())
                .eq(UserFile::getFolderFlag, userFile.getFolderFlag())
                .eq(UserFile::getUserId, userFile.getUserId())
                .eq(UserFile::getDelFlag, DelFlagEnum.NO.getCode())
                .likeLeft(UserFile::getFilename, newFilenameWithoutSuffix);
        return this.baseMapper.selectCount(wrapper);
    }
}
